home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Meeting Pearls 2
/
Meeting Pearls Vol. II (1995)(GTI - Schatztruhe)[!].iso
/
Pearls
/
dev
/
Oberon4Amiga
/
ETH_Tools
/
XE.Mod
(
.txt
)
< prev
Wrap
Oberon Text
|
1994-10-21
|
47KB
|
981 lines
Syntax10.Scn.Fnt
Syntax10i.Scn.Fnt
StampElems
Alloc
27 Sep 94
Syntax10b.Scn.Fnt
Syntax12i.Scn.Fnt
FoldElems
LineElems
Alloc
Syntax10i.Scn.Fnt
(* Other Oberons *)
Syntax10i.Scn.Fnt
(* Ceres Oberon *)
Syntax10i.Scn.Fnt
(* all Oberons without the object model (all except HP, DEC, SGI) *)
Syntax10i.Scn.Fnt
(* all Oberons with the object model (HP, DEC, SGI) *)
MODULE XE; (** SHML 10 Dec 90;
(* eXtended Edit: Supports various enhancements over usual TextFrames.Handle for programmer's purposes *)
(* adjust Delay, HandleCall *)
(* Declarations *)
IMPORT Modules, Display, Input, Files, Fonts, Texts, Viewers, Oberon, TextFrames, MenuViewers, FoldElems;
CONST
GetHandlerKey* = -210566; (** secret number to get XE.Handle **)
DefErrFile = "OberonErrors.Text"; ErrFont = "Syntax8.Scn.Fnt";
ML = 2; MM = 1; MR = 0;
CtrlB = 2X; CtrlD = 4X; CtrlE = 5X; CtrlF = 6X; BS = 08X; LF = 0AX; CtrlK = 0BX; CR = 0DX; CtrlN = 0EX;
CtrlP = 10X; CtrlT = 14X; CtrlW = 17X; CtrlX = 18X; CtrlZ = 1AX;
UpArrow = 0C1X; DnArrow = 0C2X;
MaxPat = 32;
OptionChar1 = "/"; OptionChar2 = "\"; (* character used by host Oberon System for introducing options *)
Version = "XE (SHML 27 Sep 94)";
MenuText = "XE.Menu.Text";
KeyHandler = "EditKeys.GetKeyHandler";
DefComp = "Compiler.Compile"; (* default compiler command *)
OpenCmd = "Doc.Open"; (* command used by OpenCall *)
Delay =
Input.TimeUnit DIV 2;
; (* 0.5 seconds, adjust if necessary! *)
TYPE
LongName = ARRAY 128 OF CHAR;
Name = ARRAY 32 OF CHAR;
Elem = POINTER TO ElemDesc;
ElemDesc = RECORD (Texts.ElemDesc)
err: INTEGER;
pos: LONGINT;
wide: BOOLEAN;
num: ARRAY 8 OF CHAR;
msg: LongName
END;
Element = POINTER TO ElementDesc;
ElementDesc = RECORD
compiler, ext: Name; errFile: LongName;
next: Element
END;
wr: Texts.Writer;
errT: Texts.Text; errFnt: Fonts.Font;
keyHandle: Display.Handler;
compiler, defComp: Name;
first: BOOLEAN;
root: Element;
find: RECORD
len: LONGINT;
buf: ARRAY MaxPat OF CHAR
END;
(* Support *)
PROCEDURE Str(s: ARRAY OF CHAR); BEGIN Texts.WriteString(wr, s) END Str;
PROCEDURE Ch(ch: CHAR); BEGIN Texts.Write(wr, ch) END Ch;
PROCEDURE Ln; BEGIN Texts.WriteLn(wr); Texts.Append(Oberon.Log, wr.buf) END Ln;
PROCEDURE Extension(name: ARRAY OF CHAR; VAR ext: ARRAY OF CHAR); (* get extension of name *)
VAR i, j: INTEGER;
BEGIN
i := -1; REPEAT INC(i) UNTIL name[i] = 0X;
REPEAT DEC(i) UNTIL (name[i] = ".") OR (i = 0);
IF i = 0 THEN ext[0] := 0X
ELSE j := -1; REPEAT INC(i); INC(j); ext[j] := name[i] UNTIL name[i] = 0X
END
END Extension;
PROCEDURE Append(src: ARRAY OF CHAR; VAR dest: ARRAY OF CHAR); (* append src to dest if no "." in src *)
VAR i, off: INTEGER;
BEGIN
off := -1; REPEAT INC(off) UNTIL (dest[off] = 0X) OR (dest[off] = ".");
IF dest[off] # "." THEN i := -1; REPEAT INC(i); dest[i+off] := src[i] UNTIL src[i] = 0X END
END Append;
PROCEDURE SearchPair(ext: ARRAY OF CHAR; VAR prev: Element): Element;
VAR l: Element;
BEGIN
l := root; prev := NIL; WHILE (l # NIL) & (l.ext # ext) DO prev := l; l := l.next END;
RETURN l
END SearchPair;
PROCEDURE ScanFirst(VAR s: Texts.Scanner); (* Scan first parameter *)
VAR sel: Texts.Text; beg, end, time: LONGINT;
BEGIN
Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s);
IF (s.class = Texts.Char) & (s.line = 0) & (s.c = "^") THEN
Oberon.GetSelection(sel, beg, end, time);
IF time >= 0 THEN Texts.OpenScanner(s, sel, beg); Texts.Scan(s) END
END
END ScanFirst;
PROCEDURE InstallKeyHandler;
VAR save, par: Oberon.ParList; res: INTEGER;
BEGIN
save := Oberon.Par;
NEW(par); NEW(par.frame); par.frame.X := 0; par.frame.Y := 0; par.pos := -42; (* magic *)
Oberon.Call(KeyHandler, par, FALSE, res);
IF res = 0 THEN keyHandle := Oberon.Par.frame.handle
ELSE keyHandle := NIL
END;
Oberon.Par := save; Modules.res := 0 (* bug in Modules? *)
END InstallKeyHandler;
PROCEDURE OpenText(VAR t: Texts.Text; VAR name: ARRAY OF CHAR;
s: Texts.Scanner; default, ext1, ext2: ARRAY OF CHAR);
VAR extName: LongName; i, len: INTEGER;
PROCEDURE Extend(VAR str: ARRAY OF CHAR; with: ARRAY OF CHAR); (* extend str with with *)
VAR ls, le: INTEGER;
BEGIN
ls := -1; REPEAT INC(ls) UNTIL str[ls] = 0X;
le := -1; REPEAT INC(le) UNTIL with[le] = 0X;
IF ls <= LEN(str)-le THEN
INC(ls, le); REPEAT str[ls] := with[le]; DEC(ls); DEC(le) UNTIL le = -1
END
END Extend;
PROCEDURE Try(): BOOLEAN; (* try opening name with ext1 or ext2 appended to it *)
BEGIN
COPY(name, extName); Extend(extName, ext1); t := TextFrames.Text(extName);
IF t.len = 0 THEN COPY(name, extName); Extend(extName, ext2); t := TextFrames.Text(extName) END;
RETURN t.len > 0
END Try;
BEGIN
IF first THEN first := FALSE; Str(Version); Ln; InstallKeyHandler END; (* write a startup message to the Log (once) *)
find.len := 0;
IF s.class = Texts.String THEN
t := TextFrames.Text(s.s);
name[0] := '"';
FOR i := 0 TO s.len-1 DO name[i+1] := s.s[i] END;
name[i] := '"'; name[i+1] := 0X
ELSIF s.class # Texts.Name THEN t := TextFrames.Text(default); COPY(default, name)
ELSE
COPY(s.s, name); t := TextFrames.Text(name); (* use original name *)
IF t.len = 0 THEN (* name doesn't exist *)
IF Try() THEN COPY(extName, name) (* use extended name *)
ELSE
len := s.len; REPEAT DEC(len) UNTIL (name[len] = ".") OR (len = 0);
IF len # 0 THEN (* name[len] = "." *)
i := -1; (* copy appended name to pattern for Edit.Show *)
REPEAT INC(i); find.buf[i] := name[i+len+1] UNTIL find.buf[i] = 0X;
find.buf[i] := "*"; find.buf[i+1] := 0X; find.len := i+1;
name[len] := 0X; (* delete extension, try with trimmed name *)
IF Try() THEN COPY(extName, name) (* use extended name *)
ELSE COPY(s.s, name) (* use original name with empty text *)
END
END
END
END
END
END OpenText;
PROCEDURE Show(f: TextFrames.Frame; pos: LONGINT);
VAR end, delta: LONGINT;
BEGIN
delta := 200; end := TextFrames.Pos(f, f.X+f.W, f.Y);
WHILE ((f.org > pos) OR (pos >= end)) & (f.org # end) DO
TextFrames.Show(f, pos-delta); DEC(delta, 20);
end := TextFrames.Pos(f, f.X+f.W, f.Y)
END
END Show;
PROCEDURE GetOptions(VAR s: Texts.Scanner; VAR options: ARRAY OF CHAR);
VAR pos: LONGINT; i: INTEGER; ch: CHAR; r: Texts.Reader;
BEGIN
IF (s.class # Texts.Char) OR (s.c # OptionChar1) & (s.c # OptionChar2) THEN options[0] := 0X
ELSE
pos := Texts.Pos(s);
options[0] := s.c; ch := s.nextCh; i := 1; r := s;
WHILE ((ch >= "0") & (ch <= "9") OR (ch >= "a") & (ch <= "z")) & (i < LEN(options)-1) DO
options[i] := ch; INC(i); Texts.Read(r, ch)
END;
options[i] := 0X; pos := pos+(i-1);
WHILE Texts.Pos(s) < pos DO Texts.Scan(s) END; Texts.Scan(s)
END
END GetOptions;
PROCEDURE MenuFrame(name, menu: ARRAY OF CHAR; line: INTEGER): TextFrames.Frame;
(* open MenuText and if existant get lineth textline (counting starts with 0) as menuline *)
VAR mf: TextFrames.Frame; buf: Texts.Buffer; t: Texts.Text; r: Texts.Reader; start, end: LONGINT; ch: CHAR;
BEGIN
IF Files.Old(MenuText) = NIL THEN mf := TextFrames.NewMenu(name, menu)
ELSE
mf := TextFrames.NewMenu(name, "");
NEW(t); Texts.Open(t, MenuText);
Texts.OpenReader(r, t, 0);
REPEAT (* skip line lines *)
start := Texts.Pos(r);
REPEAT Texts.Read(r, ch) UNTIL r.eot OR (ch = 0DX);
DEC(line)
UNTIL line < 0;
IF r.eot THEN end := t.len ELSE end := Texts.Pos(r)-1 END;
NEW(buf); Texts.OpenBuf(buf); Texts.Save(t, start, end, buf); Texts.Append(mf.text, buf)
END;
RETURN mf
END MenuFrame;
PROCEDURE NoNotify(t: Texts.Text; op: INTEGER; beg, end: LONGINT);
END NoNotify;
PROCEDURE BackRead(VAR r: Texts.Reader; t: Texts.Text; VAR ch: CHAR);
VAR p: LONGINT;
BEGIN
p := Texts.Pos(r);
IF p > 0 THEN Texts.OpenReader(r, t, p-1); Texts.Read(r, ch); Texts.OpenReader(r, t, p-1)
ELSE ch := 0X
END
END BackRead;
PROCEDURE InWordSet(ch: CHAR): BOOLEAN;
BEGIN
RETURN (("0" <= ch) & (ch <= "9") OR ("A" <= ch) & (ch <= "Z")
OR ("a" <= ch) & (ch <= "z") OR (80X <= ch) & (ch <= 95X))
END InWordSet;
PROCEDURE InNameSet(ch: CHAR): BOOLEAN;
BEGIN RETURN (ch = ".") OR InWordSet(ch)
END InNameSet;
PROCEDURE Find(t: Texts.Text; beg: LONGINT; VAR end: LONGINT);
VAR r: Texts.Reader; i, j, b, e: INTEGER; ch: CHAR; ref: ARRAY MaxPat OF CHAR; (* ref [b..e) is readback buffer *)
BEGIN
Texts.OpenReader(r, t, beg); Texts.Read(r, ch); i := 0; ref[0] := ch; j := 0; b := 0; e := 1;
WHILE ~r.eot & (i < find.len) DO
IF (find.buf[i] = ch) OR (i = find.len-1) & (ch = "-") THEN (* detect Name* and Name- *)
INC(i); j := (j + 1) MOD MaxPat
ELSE i := 0; b := (b + 1) MOD MaxPat; j := b
END;
IF j # e THEN ch := ref[j]
ELSE Texts.Read(r, ch); ref[j] := ch; e := (e + 1) MOD MaxPat; INC(beg)
END
END;
IF i = find.len THEN end := beg ELSE end := -1 END
END Find;
PROCEDURE WordBounds(t: Texts.Text; VAR beg, end: LONGINT; name: BOOLEAN);
VAR r: Texts.Reader; ch: CHAR;
BEGIN
Texts.OpenReader(r, t, beg);
REPEAT Texts.Read(r, ch)
UNTIL r.eot OR (name & ~InNameSet(ch)) OR (~name & ~InWordSet(ch));
IF r.eot THEN end := t.len ELSE end := Texts.Pos(r)-1 END;
Texts.OpenReader(r, t, beg);
REPEAT BackRead(r, t, ch)
UNTIL (name & ~InNameSet(ch)) OR (~name & ~InWordSet(ch));
IF ch = 0X THEN beg := 0 ELSE beg := Texts.Pos(r)+1 END
END WordBounds;
PROCEDURE EndOfLine(f: TextFrames.Frame; VAR loc: TextFrames.Location; org: LONGINT; VAR end: LONGINT);
BEGIN
WHILE (end < f.text.len) & (loc.org <= org) DO INC(end, 30); TextFrames.LocatePos(f, end, loc) END;
IF (end >= f.text.len) & (loc.org <= org) THEN end := f.text.len
ELSE WHILE loc.org > org DO end := loc.org; TextFrames.LocatePos(f, end-1, loc) END
END
END EndOfLine;
PROCEDURE TrackSelection(f: TextFrames.Frame; VAR x, y: INTEGER; VAR keysum: SET);
VAR
keys: SET;
beg, end, begW, endW, begN, endN, pos: LONGINT; loc, loc1: TextFrames.Location;
v: Viewers.Viewer; upper: TextFrames.Frame;
r: Texts.Reader; ch: CHAR;
BEGIN
v := Viewers.This(f.X, f.Y); v := v.next(Viewers.Viewer);
IF (v.dsc # NIL) & (v.dsc.next # NIL) & (v.dsc.next IS TextFrames.Frame) THEN
upper := v.dsc.next(TextFrames.Frame);
IF upper.hasSel & (upper.text = f.text) THEN
TextFrames.LocateLine(upper, upper.bot, loc);
IF (upper.selbeg.pos < loc.org) & (upper.org < upper.selend.pos)
& (upper.selbeg.pos <= TextFrames.Pos(f, x, y)) THEN
TextFrames.SetSelection(f, upper.selbeg.pos, TextFrames.Pos(f, x, y)+1)
ELSE TextFrames.RemoveSelection(upper); upper := NIL
END
ELSE upper := NIL
END
ELSE upper := NIL
END;
IF upper = NIL THEN
pos := TextFrames.Pos(f, x, y);
IF f.hasSel & (Oberon.Time() < f.time+Delay) THEN
beg := f.selbeg.pos; end := f.selend.pos;
IF (beg+1 = end) & (pos = beg) THEN (* one char selected, mouse on same character *)
TextFrames.LocatePos(f, beg, loc); TextFrames.LocatePos(f, end, loc1);
Texts.OpenReader(r, f.text, beg); Texts.Read(r, ch);
IF (end = f.text.len) OR (loc.org # loc1.org) OR ~InNameSet(ch) THEN (* extend to whole line *)
EndOfLine(f, loc1, loc.org, end);
TextFrames.SetSelection(f, loc.org, end)
ELSE (* (end # f.text.len) & (loc.org = loc1.org) & InNameSet(ch) *)
begW := pos; endW := pos+1; WordBounds(f.text, begW, endW, ch = ".");
begN := pos; endN := pos+1; WordBounds(f.text, begN, endN, TRUE);
IF (begW = beg) & (endW = end) THEN
IF (begN = beg) & (endN = end) THEN
(* single char InNameSet -> select line *)
EndOfLine(f, loc1, loc.org, end);
TextFrames.SetSelection(f, loc.org, end)
ELSE TextFrames.SetSelection(f, begN, endN) (* name *)
END
ELSE TextFrames.SetSelection(f, begW, endW) (* word *)
END
END
ELSIF (beg <= pos) & (pos < end) THEN (* mouse within selection *)
TextFrames.LocatePos(f, beg, loc); TextFrames.LocatePos(f, end-1, loc1);
IF loc.org = loc1.org THEN (* selection is at most one line *)
begW := pos; endW := pos+1; WordBounds(f.text, begW, endW, FALSE);
begN := pos; endN := pos+1; WordBounds(f.text, begN, endN, TRUE);
IF (begW = beg) & (endW = end) & ((begN < beg) OR (end < endN)) THEN
(* word selected -> extend to name *)
TextFrames.SetSelection(f, begN, endN)
ELSE (* name selected -> extend to line *)
endN := loc1.pos; EndOfLine(f, loc1, loc.org, endN);
IF (loc.org # beg) OR (endN # end) THEN TextFrames.SetSelection(f, loc.org, endN)
ELSE TextFrames.SetSelection(f, pos, pos+1) (* select single char *)
END
END
ELSE TextFrames.SetSelection(f, pos, pos+1) (* not same line -> select single char *)
END
ELSE TextFrames.SetSelection(f, pos, pos+1) (* not within selection -> select single char *)
END
ELSE TextFrames.SetSelection(f, pos, pos+1) (* no selection or time out -> select single char *)
END; (* f.hasSel & ... *)
end := f.selend.pos
ELSE end := upper.selbeg.pos
END; (* upper = NIL *)
REPEAT
Input.Mouse(keys, x, y); keysum := keysum+keys; Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y);
pos := TextFrames.Pos(f, x, y)+1;
IF f.hasSel THEN
IF pos >= end THEN TextFrames.SetSelection(f, f.selbeg.pos, pos);
IF upper # NIL THEN
TextFrames.SetSelection(upper, upper.selbeg.pos, pos); upper.selend.pos := f.selend.pos
END
END
ELSE TextFrames.SetSelection(f, TextFrames.Pos(f, x, y), TextFrames.Pos(f, x, y)+1)
END
UNTIL keys = {};
IF upper # NIL THEN f.selbeg.pos := upper.selbeg.pos END
END TrackSelection;
PROCEDURE CaretVisible(f: TextFrames.Frame; pos: LONGINT): BOOLEAN;
BEGIN RETURN f.hasCar & (f.carloc.y >= f.bot) & (f.carloc.pos = pos)
END CaretVisible;
PROCEDURE MoveTextStretch(from: Texts.Text; to: TextFrames.Frame; beg, end, pos: LONGINT);
VAR len: LONGINT;
BEGIN
(* only if other text or target pos not within selection *)
IF ((from # to.text) OR (pos < beg) OR (end < pos)) THEN
len := end-beg;
IF (from = to.text) & (end < pos) THEN DEC(pos, len) END; (* dec caret pos by length of sel *)
Texts.Save(from, beg, end, wr.buf); Texts.Delete(from, beg, end); Texts.Insert(to.text, pos, wr.buf);
TextFrames.SetCaret(to, pos+len);
IF CaretVisible(to, pos+len) THEN TextFrames.SetSelection(to, pos, pos+len) END
END
END MoveTextStretch;
PROCEDURE MoveSelection(f: TextFrames.Frame; x, y: INTEGER; keySum: SET);
VAR keys: SET; v: Viewers.Viewer; target: TextFrames.Frame;
BEGIN
REPEAT Input.Mouse(keys, x, y); keySum := keySum+keys; Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y)
UNTIL (keys = {}) OR (keySum # {MM});
IF (keys # {}) & (keySum = {MM, ML}) THEN
v := Viewers.This(x, y);
IF (v.dsc # NIL) & (v.dsc.next # NIL) & (v.dsc.next IS TextFrames.Frame) THEN
target := v.dsc.next(TextFrames.Frame);
Oberon.PassFocus(v); TextFrames.TrackCaret(target, x, y, keySum);
IF keySum = {MM, ML} THEN
MoveTextStretch(f.text, target, f.selbeg.pos, f.selend.pos, target.carloc.pos)
END
END
END
END MoveSelection;
PROCEDURE OpenCall(f: TextFrames.Frame; x, y: INTEGER; pos: LONGINT);
VAR s: Texts.Scanner; par: Oberon.ParList; loc: TextFrames.Location; beg, end, newPos: LONGINT; res: INTEGER;
BEGIN
TextFrames.LocateChar(f, x, y, loc); newPos := loc.pos;
REPEAT beg := newPos; WordBounds(f.text, beg, end, TRUE); DEC(newPos)
UNTIL (beg < end) OR (newPos < pos);
IF beg < end THEN
Texts.OpenScanner(s, f.text, beg); Texts.Scan(s);
IF (s.line = 0) & (s.class = Texts.Name) THEN
NEW(par); par.frame := f; par.text := f.text; par.pos := beg;
Oberon.Call(OpenCmd, par, FALSE, res)
END
END
END OpenCall;
PROCEDURE HandleCall(f: TextFrames.Frame; pos: LONGINT; new: BOOLEAN);
VAR s: Texts.Scanner; par: Oberon.ParList; res, i, j: INTEGER;
BEGIN
Texts.OpenScanner(s, f.text, pos); Texts.Scan(s);
IF (s.class = Texts.Name) & (s.line = 0) THEN
i := 0; WHILE (i < s.len) & (s.s[i] # ".") DO INC(i) END;
j := i+1; WHILE (j < s.len) & (s.s[j] # ".") DO INC(j) END;
IF (j >= s.len) & (s.s[i] = ".") THEN
NEW(par); par.frame := f; par.text := f.text; par.pos := pos+s.len;
Oberon.Call(s.s, par, new, res);
IF res > 0 THEN (* not-object model error messages *)
IF res = 1 THEN Str(Modules.importing); Str(" not found")
ELSIF res = 2 THEN Str(Modules.importing); Str(" not an obj-file")
ELSIF res = 3 THEN Str(Modules.importing); Str(" imports "); Str(Modules.imported); Str(" with bad key")
ELSIF res = 4 THEN Str(Modules.importing); Str(" corrupted obj file")
ELSIF res = 5 THEN Str(s.s); Str(" command not found")
ELSIF res = 6 THEN Str(Modules.importing); Str(" has too many imports")
ELSIF res = 7 THEN Str(Modules.importing); Str(" not enough space")
END;
Ln
ELSIF res < 0 THEN
INC(i); WHILE i < s.len DO Ch(s.s[i]); INC(i) END;
Str(" not found"); Ln
END
IF res > 0 THEN (* object model error messages *)
IF res = 1 THEN Str(Modules.importing); Str(" module not found")
ELSIF res = 2 THEN Str(Modules.importing); Str(" not an obj-file")
ELSIF res = 3 THEN
Str(Modules.importing); Str(" imports "); Str(Modules.objmode); Ch(" ");
Str(Modules.imported); Ch(".");
Str(Modules.object); Str(" with bad fingerprint")
ELSIF res = 4 THEN Str(Modules.importing); Str(" corrupted obj-file")
ELSIF res = 5 THEN Str(s.s); Str(" command not found")
ELSIF res = 7 THEN Str(Modules.importing); Str(" not enough space")
ELSIF res = 10 THEN
Str(Modules.importing); Str(" imports "); Str(Modules.objmode); Ch(" ");
Str(Modules.imported); Ch(".");
Str(Modules.object); Str(", not found")
ELSIF res = 11 THEN Str(Modules.importing); Str(" too many open files")
END;
Ln
ELSIF res < 0 THEN
INC(i); WHILE i < s.len DO Ch(s.s[i]); INC(i) END;
Str(" not found"); Ln
END
END
END
END HandleCall;
(* Error Element *)
PROCEDURE ElemWidth(e: Elem): INTEGER;
VAR pat: Display.Pattern; i, px, dx, x, y, w, h: INTEGER; str: LongName;
BEGIN
i := 0; px := 0;
IF e.wide THEN COPY(e.msg, str) ELSE COPY(e.num, str) END;
WHILE str[i] # 0X DO
Display.GetChar(errFnt.raster, str[i], dx, x, y, w, h, pat); INC(px, dx); INC(i)
END;
RETURN px+6
END ElemWidth;
PROCEDURE UpdateErr(e: Elem);
VAR t: Texts.Text;
BEGIN (* precondition: e.pos is correct *)
t := Texts.ElemBase(e); t.notify(t, Texts.replace, e.pos, e.pos+1)
END UpdateErr;
PROCEDURE ShowErrMsg(e: Elem; col: SHORTINT; x0, y0, dw: INTEGER);
VAR
pat: Display.Pattern; i, px, rm, dx, x, y, w, h: INTEGER;
ch: CHAR; str: LongName;
BEGIN
IF e.wide THEN COPY(e.msg, str) ELSE COPY(e.num, str) END;
i := 0; px := x0+3; rm := x0+dw-3; INC(y0, 2);
LOOP
ch := str[i]; INC(i);
IF ch = 0X THEN EXIT END;
Display.GetChar(errFnt.raster, ch, dx, x, y, w, h, pat);
IF px+dx > rm THEN EXIT END;
Display.CopyPattern(col, pat, px+x, y0+y, Display.invert); INC(px, dx)
END
END ShowErrMsg;
PROCEDURE DeleteErrElems(t: Texts.Text);
VAR r: Texts.Reader; pos: LONGINT;
BEGIN
Texts.OpenReader(r, t, 0); Texts.ReadElem(r);
WHILE r.elem # NIL DO
IF r.elem IS Elem THEN pos := Texts.Pos(r); Texts.Delete(t, pos-1, pos); Texts.OpenReader(r, t, pos) END;
Texts.ReadElem(r)
END
END DeleteErrElems;
PROCEDURE ElemHandle(e: Texts.Elem; VAR msg: Texts.ElemMsg);
VAR copy: Elem; w, h: INTEGER; keys, keySum: SET;
PROCEDURE Expand(el: Elem);
VAR s: Texts.Scanner; n: INTEGER; ch: CHAR;
BEGIN
IF el.msg[0] = 0X THEN
Texts.OpenScanner(s, errT, 0);
REPEAT
s.line := 0;
REPEAT Texts.Scan(s) UNTIL s.eot OR (s.line # 0)
UNTIL s.eot OR (s.class = Texts.Int) & (s.i = el.err);
IF ~s.eot THEN
Texts.Read(s, ch); n := 0;
WHILE ~s.eot & (ch # CR) & (n+1 < LEN(el.msg)) DO el.msg[n] := ch; INC(n); Texts.Read(s, ch) END;
el.msg[n] := 0X
END
END;
el.wide := TRUE;
el.W := LONG(ElemWidth(el))*TextFrames.Unit
END Expand;
BEGIN
WITH e: Elem DO
WITH msg: TextFrames.DisplayMsg DO
IF ~msg.prepare THEN
w := SHORT(e.W DIV TextFrames.Unit); h := SHORT(e.H DIV TextFrames.Unit);
Display.ReplConst(Display.white, msg.X0+1, msg.Y0+2, w-2, h, Display.replace);
ShowErrMsg(e, msg.col, msg.X0, msg.Y0+2, w)
END
| msg: TextFrames.TrackMsg DO (* a mouse click hit the element *)
IF msg.keys = {MM} THEN
w := SHORT(e.W DIV TextFrames.Unit); h := SHORT(e.H DIV TextFrames.Unit);
Oberon.RemoveMarks(msg.X0, msg.Y0, w, h);
Display.ReplConst(Display.white, msg.X0+2, msg.Y0+3, w-4, h-2, Display.invert);
keySum := msg.keys;
REPEAT
Input.Mouse(keys, msg.X, msg.Y); keySum := keySum+keys;
Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, msg.X, msg.Y)
UNTIL keys = {};
Display.ReplConst(Display.white, msg.X0+2, msg.Y0+3, w-4, h-2, Display.invert);
e.pos := msg.pos;
IF keySum = {MM} THEN (* expand/reduce element *)
IF ~e.wide THEN Expand(e)
ELSE e.wide := FALSE; e.W := LONG(ElemWidth(e))*TextFrames.Unit
END;
UpdateErr(e)
END;
msg.keys := {}
END
| msg: Texts.CopyMsg DO (* copy element *)
NEW(copy); Texts.CopyElem(e, copy); copy.err := e.err; copy.pos := e.pos; copy.wide := e.wide;
copy.num := e.num; copy.msg := e.msg; msg.e := copy
ELSE
END
END
END ElemHandle;
PROCEDURE InsertErrAt(t: Texts.Text; pos: LONGINT; err: INTEGER);
VAR e: Elem; h: ARRAY 8 OF CHAR; j, k: INTEGER;
BEGIN
NEW(e); e.H := 3*TextFrames.mm; e.handle := ElemHandle;
e.err := err; e.msg := ""; e.wide := FALSE;
k := 0; REPEAT h[k] := CHR(err MOD 10 + ORD("0")); err := err DIV 10; INC(k) UNTIL err = 0;
j := 0; REPEAT DEC(k); e.num[j] := h[k]; INC(j) UNTIL k = 0;
e.num[j] := 0X;
e.W := LONG(ElemWidth(e))*TextFrames.Unit;
Texts.WriteElem(wr, e); Texts.Insert(t, pos, wr.buf)
END InsertErrAt;
PROCEDURE MarkErrors(f: TextFrames.Frame; t: Texts.Text; beg: LONGINT);
VAR s: Texts.Scanner; pos, delta: LONGINT; err: INTEGER; log: Texts.Text; error: BOOLEAN;
BEGIN
DeleteErrElems(f.text);
log := Oberon.Log; pos := log.len;
Texts.OpenScanner(s, log, beg); REPEAT Texts.Scan(s) UNTIL s.eot OR (s.class = Texts.Name) & (s.s = "pos");
IF (s.class = Texts.Name) & (s.s = "pos") THEN
delta := 0;
LOOP
s.line := 0;
REPEAT Texts.Scan(s) UNTIL s.eot OR (s.line # 0) OR (s.class = Texts.Int);
IF s.eot OR (s.line # 0) THEN EXIT END;
pos := s.i;
Texts.Scan(s); error := s.s = "err";
REPEAT Texts.Scan(s) UNTIL s.eot OR (s.line # 0) OR (s.class = Texts.Int);
IF s.eot OR (s.line # 0) THEN EXIT END;
err := SHORT(s.i);
(* display errors, but warnings only if it's the Analyzer *)
IF error OR (compiler = "Analyzer.Analyze") THEN InsertErrAt(t, pos+delta, err); INC(delta) END;
REPEAT Texts.Scan(s) UNTIL s.eot OR (s.line # 0)
END
END
END MarkErrors;
PROCEDURE ErrCheck(e: Texts.Elem): BOOLEAN;
BEGIN RETURN e IS Elem
END ErrCheck;
(* Handler *)
PROCEDURE Handle(f: Display.Frame; VAR msg: Display.FrameMsg);
VAR
tf, ff: TextFrames.Frame;
t, sel: Texts.Text; loc: TextFrames.Location; copyOver: Oberon.CopyOverMsg;
r: Texts.Reader; handled: BOOLEAN; ch: CHAR; x, y: INTEGER;
pos, beg, end, len, time: LONGINT; keySum: SET;
PROCEDURE PartialFolds(text: Texts.Text; b, e: LONGINT): BOOLEAN;
CONST leftMode = {FoldElems.colLeft, FoldElems.expLeft, FoldElems.tempLeft, FoldElems.findLeft};
VAR level: INTEGER;
BEGIN
level := 0; Texts.OpenReader(r, text, b); Texts.ReadElem(r);
WHILE (r.elem # NIL) & (Texts.Pos(r) <= e) DO
IF r.elem IS FoldElems.Elem THEN
IF r.elem(FoldElems.Elem).mode IN leftMode THEN INC(level) ELSE DEC(level) END
END;
Texts.ReadElem(r)
END;
RETURN level # 0
END PartialFolds;
PROCEDURE ThisSubFrame(x, y: INTEGER): Display.Frame;
VAR sf: Display.Frame;
BEGIN
sf := f.dsc;
WHILE (sf # NIL) & ((x < sf.X) OR (x >= sf.X+sf.W) OR (y < sf.Y) OR (y >= sf.Y+sf.H)) DO sf := sf.next END;
RETURN sf
END ThisSubFrame;
BEGIN
tf := f(TextFrames.Frame);
IF keyHandle # NIL THEN keyHandle(tf, msg) END;
t := tf.text; handled := TRUE;
WITH msg: Oberon.InputMsg DO
IF (msg.id = Oberon.track) & (msg.X >= tf.X+tf.barW) & (ThisSubFrame(msg.X, msg.Y) = NIL) THEN
IF ML IN msg.keys THEN
Oberon.PassFocus(Viewers.This(tf.X, tf.Y)); TextFrames.TrackCaret(tf, x, y, keySum);
IF (keySum = {ML, MM}) & tf.hasCar THEN
Oberon.GetSelection(sel, beg, end, time);
IF time >= 0 THEN
Texts.Save(sel, beg, end, wr.buf); len := end-beg; pos := tf.carloc.pos;
Texts.Insert(tf.text, pos, wr.buf); TextFrames.SetCaret(tf, pos+len);
IF CaretVisible(tf, pos+len) THEN TextFrames.SetSelection(tf, pos, pos+len) END
END
ELSIF (keySum = {ML, MR}) & tf.hasCar & (tf.carloc.pos < tf.text.len) THEN
Oberon.GetSelection(sel, beg, end, time);
IF time >= 0 THEN
Texts.OpenReader(r, tf.text, tf.carloc.pos); Texts.Read(r, ch);
Texts.ChangeLooks(sel, beg, end, {0, 1, 2}, r.fnt, r.col, r.voff)
END
END
ELSIF MM IN msg.keys THEN
x := msg.X; y := msg.Y; pos := TextFrames.Pos(tf, x, y);
IF tf.hasSel & (tf.selbeg.pos <= pos) & (pos < tf.selend.pos) THEN MoveSelection(tf, x, y, msg.keys)
ELSE
Texts.OpenReader(r, t, pos); Texts.ReadElem(r);
IF (r.elem = NIL) OR (Texts.Pos(r) # pos+1) THEN (* no elem at this position *)
keySum := msg.keys; TextFrames.TrackWord(tf, x, y, pos, keySum);
IF (keySum = {MM}) OR (keySum = {MM, ML}) THEN HandleCall(tf, pos, keySum = {MM, ML})
ELSIF keySum = {MM, MR} THEN OpenCall(tf, x, y, pos)
END
ELSE handled := FALSE
END
END
ELSIF MR IN msg.keys THEN
TrackSelection(tf, msg.X, msg.Y, msg.keys);
IF (msg.keys = {MM, MR}) & tf.hasSel & ~PartialFolds(tf.text, tf.selbeg.pos, tf.selend.pos) THEN
copyOver.text := tf.text; copyOver.beg := tf.selbeg.pos; copyOver.end := tf.selend.pos;
len := copyOver.end-copyOver.beg;
IF (Oberon.FocusViewer IS MenuViewers.Viewer) & (Oberon.FocusViewer.dsc.next # NIL)
& (Oberon.FocusViewer(MenuViewers.Viewer).dsc.next IS TextFrames.Frame) THEN
ff := Oberon.FocusViewer.dsc.next(TextFrames.Frame); pos := ff.carloc.pos
ELSE ff := NIL
END;
Oberon.FocusViewer.handle(Oberon.FocusViewer, copyOver);
IF (ff # NIL) & CaretVisible(ff, pos+len) THEN TextFrames.SetSelection(ff, pos, pos+len) END
ELSIF (msg.keys = {ML, MR}) & tf.hasSel & ~PartialFolds(tf.text, tf.selbeg.pos, tf.selend.pos) THEN
Oberon.PassFocus(Viewers.This(tf.X, tf.Y));
Texts.Delete(tf.text, tf.selbeg.pos, tf.selend.pos); TextFrames.SetCaret(tf, tf.selbeg.pos)
END
ELSE handled := FALSE
END
ELSIF (msg.id = Oberon.consume) & tf.hasCar THEN
loc := tf.carloc; pos := loc.pos;
CASE msg.ch OF
| CR: msg.ch := LF; handled := FALSE (* switch CR <-> LF *)
| LF: msg.ch := CR; handled := FALSE
| BS, CtrlD: IF pos < t.len THEN Texts.Delete(t, pos, pos+1); TextFrames.SetCaret(tf, pos) END
| DnArrow, CtrlN:
IF loc.y-loc.dy <= tf.Y+tf.bot THEN (* at bottom of f *)
TextFrames.Show(tf, TextFrames.Pos(tf, loc.x, tf.Y+tf.bot+tf.H-tf.top));
TextFrames.SetCaret(tf, TextFrames.Pos(tf, loc.x+1, loc.y))
ELSE
y := loc.y-loc.dy;
REPEAT pos := TextFrames.Pos(tf, loc.x+1, y); DEC(y)
UNTIL (pos # loc.pos) OR (pos >= t.len) OR (y <= tf.Y+tf.bot);
TextFrames.SetCaret(tf, pos)
END
| UpArrow, CtrlP:
IF loc.org = tf.org THEN (* top of frame *)
IF tf.org > 0 THEN
pos := tf.org-1; TextFrames.Show(tf, pos);
TextFrames.SetCaret(tf, TextFrames.Pos(tf, loc.x+1, tf.Y+tf.H))
END
ELSE (* not at top *)
y := loc.y+loc.dy;
REPEAT pos := TextFrames.Pos(tf, loc.x+1, y); INC(y)
UNTIL (pos # loc.pos) OR (y >= tf.Y+tf.H);
TextFrames.SetCaret(tf, pos)
END
| CtrlT:
IF pos > 1 THEN (* exchange this with previous char *)
Texts.Save(t, pos-2, pos-1, wr.buf);
Texts.Delete(t, pos-2, pos-1); Texts.Insert(t, pos-1, wr.buf);
TextFrames.SetCaret(tf, pos)
END
| CtrlF:
IF pos < t.len THEN (* move one word forward *)
Texts.OpenReader(r, t, pos); Texts.Read(r, ch);
WHILE ~r.eot & (ch <= " ") & (ch # Texts.ElemChar) DO Texts.Read(r, ch) END;
IF r.eot THEN pos := t.len
ELSE
IF ~InWordSet(ch) THEN pos := Texts.Pos(r)
ELSE
REPEAT Texts.Read(r, ch) UNTIL r.eot OR ~InWordSet(ch);
IF r.eot THEN pos := t.len ELSE pos := Texts.Pos(r)-1 END;
TextFrames.LocatePos(tf, pos, loc);
IF loc.y <= tf.Y THEN TextFrames.Show(tf, pos) END; (* at bottom of f *)
END
END;
TextFrames.SetCaret(tf, pos)
END
| CtrlB:
IF pos > 0 THEN (* move one word backward *)
Texts.OpenReader(r, t, pos);
REPEAT BackRead(r, t, ch) UNTIL (Texts.Pos(r) = 0) OR (ch > " ") OR (ch = Texts.ElemChar);
IF Texts.Pos(r) = 0 THEN pos := 0
ELSE
IF ~InWordSet(ch) THEN pos := Texts.Pos(r)
ELSE
REPEAT BackRead(r, t, ch) UNTIL (Texts.Pos(r) = 0) OR ~InWordSet(ch);
IF Texts.Pos(r) = 0 THEN pos := 0 ELSE pos := Texts.Pos(r)+1 END
END
END;
IF pos < tf.org THEN TextFrames.Show(tf, pos) END;
TextFrames.SetCaret(tf, pos)
END
| CtrlE:
IF pos < t.len THEN (* move to end of (next) line *)
Texts.OpenReader(r, t, pos); Texts.Read(r, ch);
IF ~r.eot & (ch = CR) THEN Texts.Read(r, ch) END;
WHILE ~r.eot & (ch # CR) DO Texts.Read(r, ch) END;
IF r.eot THEN TextFrames.SetCaret(tf, t.len) ELSE TextFrames.SetCaret(tf, Texts.Pos(r)-1) END
END
| CtrlW:
IF pos > 0 THEN (* move to beginning of (previous) line *)
IF pos = loc.org THEN TextFrames.LocatePos(tf, pos-1, loc) END;
TextFrames.SetCaret(tf, loc.org)
END
| CtrlK: (* delete to end of line *)
Texts.OpenReader(r, t, pos);
REPEAT Texts.Read(r, ch) UNTIL r.eot OR (ch = CR);
IF Texts.Pos(r) = pos+1 THEN Texts.Delete(t, pos, pos+1) ELSE Texts.Delete(t, pos, Texts.Pos(r)-1) END;
TextFrames.SetCaret(tf, pos)
| CtrlX: (* move selection to caret position *)
Oberon.GetSelection(sel, beg, end, time);
IF time >= 0 THEN MoveTextStretch(sel, tf, beg, end, pos) END
| CtrlZ:
IF pos < t.len THEN (* delete forward to non-char *)
Texts.OpenReader(r, t, pos); Texts.Read(r, ch);
WHILE ~r.eot & (ch <= " ") & (ch # Texts.ElemChar) DO Texts.Read(r, ch) END;
IF r.eot THEN end := t.len
ELSE
IF ~InWordSet(ch) THEN end := Texts.Pos(r)
ELSE
REPEAT Texts.Read(r, ch) UNTIL r.eot OR ~InWordSet(ch);
IF r.eot THEN end := t.len ELSE end := Texts.Pos(r)-1 END
END
END;
Texts.Delete(t, pos, end); TextFrames.SetCaret(tf, pos)
END
ELSE handled := FALSE
END (* CASE msg.ch ... *)
ELSE handled := FALSE
END (* IF msg.id = ... *)
| msg: Oberon.CopyOverMsg DO (* allow copy over only if text has no partial folds in it *)
IF ~tf.hasCar OR ~PartialFolds(msg.text, msg.beg, msg.end) THEN handled := FALSE END
ELSE handled := FALSE
END; (* WITH msg: ... *)
IF ~handled THEN TextFrames.Handle(tf, msg) END
END Handle;
(** Commands **)
PROCEDURE Open*; (** (name | "^") Open a user viewer containing a text **)
CONST
menu1 =
"System.Close System.Copy System.Grow XE.Search Edit.Replace All XE.Comp XE.Err Edit.Store ";
menu2 =
"System.Close System.Copy System.Grow Edit.Search Edit.Replace All Edit.Parcs Edit.Store ";
VAR
v: Viewers.Viewer; f: TextFrames.Frame; t: Texts.Text; s: Texts.Scanner;
name, menu: LongName; i, x, y: INTEGER; pos: LONGINT;
BEGIN
ScanFirst(s); OpenText(t, name, s, "Empty.Mod", ".Mod", ".Text");
i := 0; REPEAT INC(i) UNTIL name[i] = 0X;
IF (i > 3) & (name[i-3] = "M") & (name[i-2] = "o") & (name[i-1] = "d") THEN menu := menu1
ELSE menu := menu2
END;
Oberon.AllocateUserViewer(Oberon.Mouse.X, x, y);
f := TextFrames.NewText(t, 0);
v := MenuViewers.New(MenuFrame(name, menu, 0), f, TextFrames.menuH, x, y);
f.handle := Handle;
IF find.len > 0 THEN (* simulate Edit.Show *)
Find(t, 0, pos);
IF pos > 0 THEN
DEC(pos, find.len);
TextFrames.Show(f, pos); TextFrames.SetSelection(f, pos, pos+find.len-1);
TextFrames.SetCaret(f, pos+find.len-1)
END
END
END Open;
(* PROCEDURE OpenWide*; (** (name | "^") Open a user viewer containing a text **)
CONST
menu1 =
"System.Close System.Copy System.Grow XE.Search Edit.Replace All XE.Comp XE.Err Edit.Store | Log.Open | XE.SysOpen 57 Strip ";
menu2 =
"System.Close System.Copy System.Grow Edit.Search Edit.Replace All Edit.Parcs Edit.Store | Log.Open | XE.SysOpen 57 Strip ";
VAR
v: Viewers.Viewer; mf: TextFrames.Frame; t: Texts.Text; s: Texts.Scanner;
name, menu: ARRAY 128 OF CHAR; i, x, y: INTEGER;
BEGIN
ScanFirst(s); OpenText(t, name, s, "Empty.Mod", ".Mod", ".Text");
i := 0; REPEAT INC(i) UNTIL name[i] = 0X;
IF (i > 3) & (name[i-3] = "M") & (name[i-2] = "o") & (name[i-1] = "d") THEN menu := menu1
ELSE menu := menu2
END;
Oberon.AllocateUserViewer(Oberon.Mouse.X, x, y);
mf := MenuFrame(name, menu, 2);
Oberon.OpenTrack(Oberon.UserTrack(Oberon.Mouse.X), Oberon.DisplayWidth(Oberon.Mouse.X));
v := MenuViewers.New(mf, TextFrames.NewText(t, 0), TextFrames.menuH, x, y);
v.dsc.next.handle := Handle
END OpenWide;
PROCEDURE SysOpen*; (** [defY] (name | "^") Open a system viewer at defY **)
CONST
menuCommands = "System.Close System.Grow Edit.Parcs Edit.Store ";
VAR
v: Viewers.Viewer; t: Texts.Text;
s: Texts.Scanner; name: LongName; x, defY, y: INTEGER;
default: BOOLEAN;
BEGIN
ScanFirst(s);
IF s.class = Texts.Int THEN (* read desired Y-coordinate *)
defY := SHORT(s.i); default := TRUE; Oberon.Par.pos := Texts.Pos(s)-1; ScanFirst(s)
ELSE default := FALSE
END;
OpenText(t, name, s, "Empty.Tool", ".Tool", ".Tool");
Oberon.AllocateSystemViewer(Oberon.Mouse.X, x, y);
IF default THEN y := defY END;
v := MenuViewers.New(MenuFrame(name, menuCommands, 1), TextFrames.NewText(t, 0), TextFrames.menuH,x,y);
v.dsc.next.handle := Handle
END SysOpen;
PROCEDURE Search*; (** Search selection (in folds if viewer is marked) **)
VAR res: INTEGER;
BEGIN
IF Oberon.Pointer.on & (Oberon.Par.vwr = Oberon.MarkedViewer()) THEN
Oberon.Call("FoldElems.Search", Oberon.Par, FALSE, res)
ELSE Oberon.Call("Edit.Search", Oberon.Par, FALSE, res)
END
END Search;
PROCEDURE Err*; (** Show next error after caret **)
VAR f: TextFrames.Frame; v: Viewers.Viewer; pos: LONGINT; e: Texts.Elem;
BEGIN
IF Oberon.Par.vwr.dsc = Oberon.Par.frame THEN (* called from menu frame *)
IF (Oberon.Par.frame.next # NIL) & (Oberon.Par.frame.next IS TextFrames.Frame) THEN
f := Oberon.Par.frame.next(TextFrames.Frame)
ELSE f := NIL
END
ELSE
v := Oberon.MarkedViewer();
IF (v.dsc # NIL) & (v.dsc.next # NIL) & (v.dsc.next IS TextFrames.Frame) THEN f := v.dsc.next(TextFrames.Frame)
ELSE f := NIL
END
END;
IF f # NIL THEN
IF f.hasCar THEN pos := f.carloc.pos ELSE pos := 0 END;
FoldElems.FindElem(f.text, pos, ErrCheck, e, pos);
IF e # NIL THEN
(*TextFrames.*)Show(f, pos); e(Elem).pos := pos; UpdateErr(e(Elem));
Oberon.PassFocus(Viewers.This(f.X, f.Y)); TextFrames.SetCaret(f, pos+1)
ELSIF f.hasCar THEN TextFrames.RemoveCaret(f)
END
END
END Err;
PROCEDURE Comp*; (** [options] | "*" | "^" | {fileName [options]} Compile viewer in main frame with options
or marked viewer or list of filenames with options **)
VAR
f: TextFrames.Frame; menuT: Texts.Text; s: Texts.Scanner; v: Viewers.Viewer;
len: LONGINT; options: Name; fileName: LongName;
PROCEDURE Compile(frame: TextFrames.Frame; text: Texts.Text; name: ARRAY OF CHAR);
VAR
vwr: MenuViewers.Viewer; oldNotify: Texts.Notifier; logLen: LONGINT; x, y, h: INTEGER;
this, prev: Element; ext: Name; errorFile: LongName; res: INTEGER;
BEGIN
IF text # NIL THEN
COPY(DefErrFile, errorFile);
IF (compiler = "") & (name # "") OR (frame = NIL) THEN (* no compile command yet, check extension *)
COPY(defComp, compiler);
Extension(name, ext); this := SearchPair(ext, prev);
IF this # NIL THEN COPY(this.compiler, compiler); COPY(this.errFile, errorFile) END
END;
errT := TextFrames.Text(errorFile);
oldNotify := text.notify; text.notify := NoNotify;
FoldElems.ExpandAll(text, 0, TRUE);
IF frame = NIL THEN (* create temporary viewer *)
x := Display.Width-1; y := Display.Bottom; h := Viewers.minH; Viewers.minH := 1;
vwr := MenuViewers.New(TextFrames.NewMenu("", ""),
TextFrames.NewText(text, 0), TextFrames.menuH, x, y
);
Oberon.Pointer.X := x; Oberon.Pointer.Y := y;
Viewers.minH := h
ELSE DeleteErrElems(text)
END;
(* create new parameter text for compiler *)
Oberon.Par.text := TextFrames.Text(""); Oberon.Par.pos := 0;
Ch("*"); Str(options); Texts.Append(Oberon.Par.text, wr.buf);
Str(compiler); Ch(" "); Str(options); Texts.Append(Oberon.Log, wr.buf);
Append(".Compile", compiler); (* extend compiler command, if necessary *)
logLen := Oberon.Log.len;
Oberon.Call(compiler, Oberon.Par, FALSE, res);
IF (res = 0) & (frame # NIL) THEN MarkErrors(frame, text, logLen) END;
FoldElems.CollapseAll(text, {FoldElems.tempLeft});
IF frame = NIL THEN Viewers.Close(vwr)
ELSE text.notify := oldNotify; text.notify(text, Texts.replace, 0, text.len)
END
END
END Compile;
BEGIN
menuT := NIL;
IF Oberon.Par.vwr.dsc = Oberon.Par.frame THEN (* called from menu frame *)
IF (Oberon.Par.frame.next # NIL) & (Oberon.Par.frame.next IS TextFrames.Frame) THEN
f := Oberon.Par.frame.next(TextFrames.Frame);
menuT := Oberon.Par.frame(TextFrames.Frame).text (* menu text *)
END
ELSE (* allow XE.Comp * ... *)
ScanFirst(s);
IF (s.class = Texts.Char) & (s.c = "*") & (s.line = 0) THEN
Oberon.Par.pos := Texts.Pos(s);
v := Oberon.MarkedViewer();
IF (v IS MenuViewers.Viewer) & (v.dsc.next # NIL) & (v.dsc.next IS TextFrames.Frame) THEN
f := v.dsc.next(TextFrames.Frame);
menuT := v.dsc(TextFrames.Frame).text (* menu text *)
END
END
END;
IF menuT # NIL THEN
ScanFirst(s); compiler := ""; fileName := "";
IF (s.class = Texts.Name) & (s.line = 0) THEN (* get compiler override name *)
COPY(s.s, compiler); Texts.Scan(s)
END;
GetOptions(s, options);
IF compiler = "" THEN
Texts.OpenScanner(s, menuT, 0); Texts.Scan(s);
IF s.class = Texts.Name THEN COPY(s.s, fileName) END
END;
len := menuT.len;
Oberon.DrawCursor(Oberon.Pointer, Oberon.Star, f.X, f.Y); Oberon.FadeCursor(Oberon.Pointer);
Compile(f, f.text, fileName);
IF len # menuT.len THEN (* text was stored and got an UpdateMsg -> ! char in menu text *)
Texts.Delete(menuT, menuT.len-1, menuT.len)
END;
Err (* show first error, if any *)
ELSE (* compile file list: {fileName [options] } ~ *)
ScanFirst(s);
WHILE s.class = Texts.Name DO
COPY(s.s, fileName); Texts.Scan(s); GetOptions(s, options);
Compile(NIL, TextFrames.Text(fileName), fileName)
END
END
END Comp;
PROCEDURE Compiler*; (** [(Compiler [Ext [ErrFile]] | "^")] Install or list compiler, extension, errorfile set **)
VAR s: Texts.Scanner; line: INTEGER; errorFile: LongName; new, this, prev: Element;
BEGIN
ScanFirst(s);
IF s.class = Texts.Name THEN
line := s.line;
COPY(s.s, compiler);
Str("XE using "); Str(compiler);
Texts.Scan(s);
IF (s.class = Texts.Name) & (s.line = line) THEN
NEW(new); COPY(compiler, new.compiler); COPY(s.s, new.ext);
Texts.Scan(s);
IF (s.class = Texts.Name) & (s.line = line) THEN
COPY(s.s, errorFile); Append("Errors.Text", errorFile);
errT := TextFrames.Text(errorFile);
IF errT.len = 0 THEN errT := TextFrames.Text(DefErrFile); COPY(DefErrFile, errorFile) END
ELSE errT := TextFrames.Text(DefErrFile); COPY(DefErrFile, errorFile)
END;
COPY(errorFile, new.errFile);
this := SearchPair(new.ext, prev); (* check for duplicates *)
IF this = NIL THEN new.next := root; root := new (* new entry *)
ELSIF this.compiler # new.compiler THEN (* new entry for existing extension -> remove this *)
IF this = root THEN new.next := root.next; root := new
ELSE new.next := this.next; prev.next := new
END
END;
Str(" and "); Str(new.errFile); Str(" for *."); Str(new.ext)
ELSE COPY(compiler, defComp); Str(" as default")
END;
Ln
ELSE
Str("XE.Compiler"); Ln;
this := root;
WHILE this # NIL DO
Str(this.compiler ); Ch(" "); Str(this.errFile); Str(" *."); Str(this.ext); Ln;
this := this.next
END;
IF defComp # "" THEN Str(defComp); Ch(" "); Str(DefErrFile); Str(" *"); Ln END
END
END Compiler;
PROCEDURE GetHandler*; (** install XE.Handle in Oberon.Par.frame.handle, if Oberon.Par.pos = GetHandlerKey **)
BEGIN
IF (Oberon.Par.pos = GetHandlerKey) & (Oberon.Par.frame # NIL) THEN Oberon.Par.frame.handle := Handle END
END GetHandler;
BEGIN
Texts.OpenWriter(wr); errFnt := Fonts.This(ErrFont); root := NIL; first := TRUE; defComp := DefComp
END XE.